package com.hx.rpc.registry;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;


/**
 * @author Huathy
 * @date 2021-10-23 00:03
 * @description
 */
public class RpcRegistry {
    private int port;

    public RpcRegistry(int port) {
        this.port = port;
    }

    public void start() {
        // 在没有使用Netty的时候，使用的是ServerSocket / ServerSocketChanel
        // 在netty中对应的就是ServerBootstrap
        // Selector 主线程，work线程
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap server = new ServerBootstrap();
            server.group(bossGroup,workGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // 成功回调。在netty中将所有的业务逻辑处理全部归总到了一个队列中。
                            // 这个队列中包含了各种各样的处理逻辑。对这些处理逻辑，在netty中有一个封装对象。
                            // 这个对象就是一个无锁化，串行任务队列，Pipeline
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            // 实际就是对处理逻辑的封装。

                            // 对于自定义协议的内容，需要先进行编码、解码。用户首次连接，会将网络数据携带过来，将数据数组解析。
                            //指定解码器。必须先指定解码器，再指定编码器。解码 -> 编码是一个过程。
                            // 前面的两个解码编码器是还原InvokerProtocol对象。完整的接受对象
                            pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
                            //最大长度，偏移量(有的信息会带有信息头，信息头占一定字节，这里没有信息头，写0),int所占字节数，添加字段补长值(没有特殊处理为0)，
// public LengthFieldBasedFrameDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip) {
                            //指定解码器
                            pipeline.addLast(new LengthFieldPrepender(4));

                            //参数值处理,编码解码。下面的两个解码编码是为了还原对象中的参数(反序列化) -> 参数类型列表 private Class<?>[] params;   参数值列表 private Object[] values;
                            pipeline.addLast("encoder",new ObjectEncoder());
                            pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));

                            //前面的编解码就是完成对数据的解析
                            //最后一步，执行自己的逻辑
                            // 1. 注册，给每个对象起一个名字。对外提供服务的名字
                            // 2. 服务的位置要做登记。
                            pipeline.addLast(new RegistryHandler());
                        }
                    }).option(ChannelOption.SO_BACKLOG,128)
                    .childOption(ChannelOption.SO_KEEPALIVE,true);
            //正式启动服务。相当于用一个死循环开始轮询
            ChannelFuture future = server.bind(port).sync();
            System.out.println(" HX RPC Registry is start, listen at port : " + port);
            future.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new RpcRegistry(8888).start();
    }
}
